﻿using AForge.Video.DirectShow;
using arcsoft_usb_video.ArcFace;
using arcsoft_usb_video.Entity;
using arcsoft_usb_video.SDKUtil;
using arcsoft_usb_video.Utils;
using ArcSoftFace.SDKModels;
using ArcSoftFace.Utils;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.Caching;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace arcsoft_usb_video
{
    public partial class Form1 : Form
    {
        private VideoCaptureDevice vcd;
        private bool faceLive = false;
        private int logLines = 0;
        private readonly ArcFaceSDK arcFaceSDK = new ArcFaceSDK();
        private VideoNewFrame videoNewFrame = new VideoNewFrame();
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            VideoCaptureDeviceForm vcdf = new VideoCaptureDeviceForm();
            DialogResult dr = vcdf.ShowDialog(this);
            if(dr == DialogResult.OK)
            {
                this.vcd = vcdf.VideoDevice;
                this.vcd.NewFrame += videoNewFrame.Vcd_NewFrame;
                this.vcd.Start();
            }
            else
            {
                Close();
            }

            arcFaceSDK.InitSDK();
            InitDirectory();
            loadLocalRepositoryData();


            //Task.Factory.StartNew(() => DetectFace(), TaskCreationOptions.LongRunning);
            Task.Factory.StartNew(() => Reged(), TaskCreationOptions.LongRunning);
            Task.Factory.StartNew(() => Live(), TaskCreationOptions.LongRunning);
            Task.Factory.StartNew(() => PlayImg(), TaskCreationOptions.LongRunning);
        }

        private void PlayImg()
        {
            Stopwatch stopwatch = Stopwatch.StartNew(); 
            while (!this.IsDisposed)
            {
                Bitmap bitmap = videoNewFrame.Get_NewFrame();
                if (bitmap == null || bitmap.Width == 0){
                    Thread.Sleep(30);
                    continue;
                }

                stopwatch.Restart();
                if(this.faceLive)
                {
                    ASF_MultiFaceInfo multiFaceInfo = arcFaceSDK.DetectFaces(bitmap);
                    KeyValuePair<int, Rectangle>[]  faceRects = arcFaceSDK.MultiFaceInfoToKV(multiFaceInfo);
                    if (faceRects != null && faceRects.Length > 0)
                    {
                        using (Graphics g = Graphics.FromImage(bitmap))
                        {
                            foreach (var kv in faceRects)
                            {
                                g.DrawRectangle(string.IsNullOrEmpty(this.tip) ? Pens.Silver : Pens.Blue, kv.Value);
                            }
                        }
                    }
                }

                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.DrawString(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), DefaultFont, Brushes.Red, 10, 10);
                    if (!string.IsNullOrEmpty(this.tip))
                    {
                        g.DrawString(this.tip, DefaultFont, Brushes.Red, 10, 30);
                    }
                }

                pictureBox1.Image = bitmap;

                Console.WriteLine("人脸追踪耗时：{0}", stopwatch.ElapsedMilliseconds);
                int sleep = (int)(30 - stopwatch.ElapsedMilliseconds);
                if (sleep > 0)
                {
                    Thread.Sleep(sleep);
                }
            }
        }

        private void loadLocalRepositoryData()
        {
            if (File.Exists("data/data.xml"))
            {
                dataSet1.ReadXml("data/data.xml");
            }

            string[] files = Directory.GetFiles("data/face");
            foreach(var s in files)
            {
                if (!s.EndsWith("data")) continue;
                FileInfo fi = new FileInfo(s);
                arcFaceSDK.AddFace(fi.Name.Replace(fi.Extension,""), File.ReadAllBytes(s));
            }
        }

        private void InitDirectory()
        {
            string[] checkDirectory = { "data/face", "data/record" };
            foreach(string d in checkDirectory)
            {
                if (!Directory.Exists(d))
                {
                    Directory.CreateDirectory(d);
                }
            }
        }

        private void Live()
        {
            Thread.Sleep(2000);
            while (!this.IsDisposed)
            {
                Bitmap bitmap = videoNewFrame.Get_NewFrame();

                Stopwatch stopwatch = Stopwatch.StartNew();
                if (bitmap != null)
                {
                    using (bitmap)
                    {
                        try
                        {
                            this.faceLive = arcFaceSDK.Live(bitmap);
                            Console.WriteLine("人脸活体检测为：{0}", this.faceLive);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                    bitmap.Dispose();
                }
                Console.WriteLine("人脸活体耗时:{0}", stopwatch.ElapsedMilliseconds);
                if (stopwatch.ElapsedMilliseconds < 100)
                {
                    Thread.Sleep((int)(100 - stopwatch.ElapsedMilliseconds));
                }
            }
        }

        private void Reged()
        {
            Thread.Sleep(3000);
            while (!this.IsDisposed)
            {
                if (!this.faceLive)
                {
                    Thread.Sleep(100);
                    continue;
                }
                Stopwatch stopwatch = Stopwatch.StartNew();
                Image bitmap = videoNewFrame.Get_NewFrame();
                if (bitmap != null && bitmap.Width != 0)
                {
                    //识别图片过大时，先将图片缩放一半、不然识别时间会很长
                    if(bitmap.Width > 640)
                    {
                        Bitmap newBitmap = new Bitmap(bitmap.Width / 2, bitmap.Height /2);
                        using(Graphics g = Graphics.FromImage(newBitmap))
                        {
                            g.DrawImage(bitmap, new Rectangle(0, 0, newBitmap.Width, newBitmap.Height), new Rectangle(0, 0, bitmap.Width, bitmap.Height), GraphicsUnit.Pixel);
                        }
                        bitmap = newBitmap;
                    }

                    Console.WriteLine("人脸缩放耗时:{0}", stopwatch.ElapsedMilliseconds);
                    using (bitmap)
                    {
                        try
                        {
                            FaceInfo[] infos = arcFaceSDK.SearchFace(bitmap);
                            for(int i = 0; i < infos.Length; i++)
                            {
                                FaceInfo info = infos[i];
                                if (info == null) continue;

                                DataRowCollection drc = Table_input.Rows;
                                foreach (DataRow dr in drc)
                                {
                                    object[] arr = (object[])dr.ItemArray;
                                    if (!arr[0].Equals(info.key)) continue;

                                    string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                    string now2 = DateTime.Now.ToString("yyyyMMddHHmmss");
                                    if (MemoryCache.Default.Get(info.key) == null)
                                    {
                                        if (logLines > 100)
                                        {
                                            logLines = 0;
                                            textBox_record_log.Clear();
                                        }
                                        logLines++;
                                        this.Invoke(new EventHandler(delegate {
                                            Image image = ImageUtil.CutImage(bitmap, info.rectangle.Left, info.rectangle.Top, info.rectangle.Right, info.rectangle.Bottom);
                                            image.Save("data/record/" + now2 + ".jpeg", ImageFormat.Jpeg);
                                            label_record_no.Text = (string)arr[0];
                                            label_record_name.Text = (string)arr[1];
                                            label_record_group.Text = (string)arr[2];
                                            label_record_time.Text = now;
                                            pictureBox_record_new.Image = image;
                                            pictureBox_record_old.Image = Bitmap.FromFile((string)arr[3]);

                                            textBox_record_log.AppendText(now + "\t" + (string)arr[0] + "\t" + (string)arr[1] + "\r\n");
                                        }));
                                        Table_record.Rows.Add(arr[0], arr[1], arr[2], now);
                                        MemoryCache.Default.Add(info.key, info, DateTimeOffset.Now.AddSeconds(1));
                                    }
                                    break;
                                }
                            }
                        }
                        catch (Exception ex)
                        {

                            Console.WriteLine(ex.Message);
                        }
                    }
                    bitmap.Dispose();
                }
                Console.WriteLine("人脸识别耗时:{0}", stopwatch.ElapsedMilliseconds);
                if (stopwatch.ElapsedMilliseconds < 100)
                {
                    Thread.Sleep((int)(100 - stopwatch.ElapsedMilliseconds));
                }
            }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if(this.vcd != null)
            {
                this.vcd.Stop();
                this.vcd.WaitForStop();
            }

            dataSet1.WriteXml("data/data.xml");
        }

        //界面图像控件重绘
        Rectangle destRect = new Rectangle(0, 0, 0,0);
        Rectangle srcRect = new Rectangle(0, 0, 0, 0);
        private string tip;

        private void PictureBox_video_Paint(object sender, PaintEventArgs e)
        {
            Bitmap currentFrame = videoNewFrame.Get_NewFrame();
            if (currentFrame == null)
            {
                return;
            }

            Graphics graphics = e.Graphics;
            int pw = pictureBox1.Width;
            int ph = pictureBox1.Height;
            int cw = currentFrame.Width;
            int ch = currentFrame.Height;

            if(pw == 0 || ph == 0 || cw ==0 || ch == 0)
            {
                return;
            }

            //if (this.faceRect != null && !this.faceRect.IsEmpty)
            //{
            //    using (Graphics g = Graphics.FromImage(currentFrame))
            //    {
            //        g.DrawRectangle(string.IsNullOrEmpty(this.tip) ? Pens.Silver : Pens.Blue, this.faceRect);
            //    }
            //}

            if(this.faceRects != null && this.faceRects.Length > 0)
            {
                using (Graphics g = Graphics.FromImage(currentFrame))
                {
                    foreach (var kv in faceRects)
                    {
                        g.DrawRectangle(string.IsNullOrEmpty(this.tip) ? Pens.Silver : Pens.Blue, kv.Value);
                    }
                }
            }

            //如果图像比控件面积大，则只显示图像最中间的部分
            if (cw >= pw && ch >= ph)
            {
                int iw = (cw - pw) / 2;
                int ih = (ch - ph) / 2;
                destRect.X = 0;
                destRect.Y = 0;
                destRect.Width = pw;
                destRect.Height = ph;

                srcRect.X = iw;
                srcRect.Y = ih;
                srcRect.Width = pw;
                srcRect.Height = ph;
                //Rectangle destRect = new Rectangle(0, 0, pw, ph);
                //Rectangle srcRect = new Rectangle(iw, ih, pw, ph);
                graphics.DrawImage(currentFrame, destRect, srcRect, GraphicsUnit.Pixel);
            }

            //如果图像比控件面积小，则居中显示
            else if (cw < pw && ch < ph)
            {
                int iw = (pw - cw) / 2;
                int ih = (ph - ch) / 2;

                destRect.X = iw;
                destRect.Y = ih;
                destRect.Width = cw;
                destRect.Height = ch;

                srcRect.X = 0;
                srcRect.Y = 0;
                srcRect.Width = cw;
                srcRect.Height = ch;

                graphics.DrawImage(currentFrame, destRect, srcRect, GraphicsUnit.Pixel);
            }
            //图像比较窄，但很高 （一般不会有这种画质，但不排除手动拖拽窗口大小）
            else if(cw < pw && ch > ph)
            {
                int iw = (pw - cw) / 2;
                int ih = (ch - ph) / 2;

                destRect.X = iw;
                destRect.Y = 0;
                destRect.Width = ch;
                destRect.Height = ph;

                srcRect.X = 0;
                srcRect.Y = ih;
                srcRect.Width = cw;
                srcRect.Height = ph;

                graphics.DrawImage(currentFrame, destRect, srcRect, GraphicsUnit.Pixel);
            }
            //图像比较宽，但很低
            else if (cw > pw && ch < ph)
            {
                int iw = (cw - pw) / 2;
                int ih = (ph - ch) / 2;

                destRect.X = 0;
                destRect.Y = ih;
                destRect.Width = pw;
                destRect.Height = cw;

                srcRect.X = iw;
                srcRect.Y = 0;
                srcRect.Width = pw;
                srcRect.Height = cw;

                graphics.DrawImage(currentFrame, destRect, srcRect, GraphicsUnit.Pixel);
            }
            graphics.DrawString(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), DefaultFont, Brushes.Red, 10, 10);
            if (!string.IsNullOrEmpty(this.tip))
            {
                graphics.DrawString(this.tip, DefaultFont, Brushes.Red, 10, 30);
            }

            currentFrame.Dispose();
        }

        private Rectangle last = new Rectangle();
        private KeyValuePair<int, Rectangle>[] faceRects;
        private void Button1_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(this.tip))
            {
                return;
            }

            this.tip = "正在采集中...";
            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                int times = 1;
                for(int i = 0; i < 1000 && !this.IsDisposed; i++)
                {
                    try
                    {
                        Bitmap bitmap = videoNewFrame.Get_NewFrame();
                        if (bitmap != null)
                        {
                            if (times > 5)
                            {
                                
                                Image clone = ImageUtil.CutImage(bitmap, this.last.Left, this.last.Top, this.last.Right, this.last.Bottom);
                                clone.Save("current.jpeg", ImageFormat.Jpeg);
                                byte[] data = arcFaceSDK.FaceFeatureData(bitmap);
                                File.WriteAllBytes("current.data", data);
                                pictureBox_save.BeginInvoke(new EventHandler(delegate {pictureBox_save.Image = clone; }));
                                this.tip = "采集成功";
                                Thread.Sleep(2000);
                                this.tip = "";
                                return;
                            }

                            ASF_MultiFaceInfo multiFaceInfo = arcFaceSDK.DetectFaces(bitmap);
                            if(multiFaceInfo.faceNum > 1)
                            {
                                this.tip = "一次只允许一个人脸进行注册";
                                continue;
                            }
                            ASF_SingleFaceInfo singleFaceInfo = FaceUtil.GetMaxFace(multiFaceInfo);
                            Rectangle rectangle = new Rectangle(singleFaceInfo.faceRect.left, singleFaceInfo.faceRect.top, singleFaceInfo.faceRect.right - singleFaceInfo.faceRect.left, singleFaceInfo.faceRect.bottom - singleFaceInfo.faceRect.top);

                            if (rectangle.Width < 250)
                            {
                                this.tip = "人脸太小,请靠近一丁点";
                                continue;
                            }
                            else if (rectangle.Width > 350)
                            {
                                this.tip = "人脸太大,请远离一丁点";
                                continue;
                            }
                            else if(rectangle.X < bitmap.Width / 2 - 200)
                            {
                                this.tip = "请尽量站在中间";
                                continue;
                            }
                            else if (rectangle.X > bitmap.Width / 2 + 200)
                            {
                                this.tip = "请尽量站在中间";
                                continue;
                            }
                            else
                            {
                                this.tip = string.Format("位置正好，请正对摄像头，系统正在采集，第{0}次，共5次",times);
                            }

                            if (last.Equals(rectangle))
                            {
                                times++;
                            }
                            else
                            {
                                last = rectangle;
                                times = 1;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("人脸采集时发生异常：{0}",ex.Message);
                    }
                    finally
                    {
                        Thread.Sleep(500);
                    }
                }
                this.tip = "";
                Console.WriteLine("退出录入识别");
            }, TaskCreationOptions.LongRunning);
        }

        private void Button2_Click(object sender, EventArgs e)
        {
            if(pictureBox_save.Image == null)
            {
                MessageBox.Show("人脸不可为空，请先识别人脸");
                return;
            }

            string no_save = textBox_no_save.Text;
            if (string.IsNullOrEmpty(no_save))
            {
                MessageBox.Show("编号不可为空");
                return;
            }

            string name_save = textBox_name_save.Text;
            if (string.IsNullOrEmpty(name_save))
            {
                MessageBox.Show("姓名不可为空");
                return;
            }

            string group_save = textBox_group_save.Text;
            if (string.IsNullOrEmpty(group_save))
            {
                MessageBox.Show("部门不可为空");
                return;
            }

            File.Delete("data/face/" + no_save + ".data");
            File.Delete("data/face/" + no_save + ".jpeg");
            arcFaceSDK.AddFace(no_save, File.ReadAllBytes("current.data"));
            File.Copy("current.data", "data/face/" + no_save + ".data");
            File.Copy("current.jpeg", "data/face/" + no_save + ".jpeg");
            Table_input.Rows.Add(no_save, name_save, group_save, "data/face/" + no_save + ".jpeg");
            dataSet1.WriteXml("data/data.xml");
        }
    }
}
